package com.itextpdf.text.pdf;

import java.io.IOException;
import java.util.HashMap;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Utilities;

/**
 * 
 * Treat subset fonts.
 * SubsetFont has responsibility for making glyph subset
 * 
 * @author dogfooter
 *
 */
public class SubsetFont {
	
	private String fontFileName;
	private String text;
	
	private BaseFont bf;
	private TrueTypeFontUnicode font;
	private HashMap<Integer, int[]> longTag;
	
	// filename without path
	private String actualFileName;
	// path delimituer "/" or "\\" 
	private String pathDelimiter;
	private String extension;
	
	/**
	 * absolute path of directory that has source font file
	 */
	private String directory;
	

	/**
	 * Judge whether the font supports Hangeul or not.
	 * @param fontFileName
	 * 			source font file
	 * @return boolean
	 * 			if supports Hangeul return true or false.
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static boolean isKorean(String fontFileName) throws DocumentException, IOException {

		TrueTypeFontUnicode font = (TrueTypeFontUnicode) BaseFont.createFont(fontFileName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		
		synchronized (font.rf) {
			@SuppressWarnings("deprecation")
			RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(font.rf);
			long len = rf2.length();
			long offset = 0;
			long length = 0;
			
			long platformID = 0;
			long languageID = 0;
			
			long count = 0;
			
			byte[] b = new byte[(int)len];
			try {
				rf2.seek(0);
				rf2.readFully(b);
				
				// read offset and length of name table
				for (int i = 12; i < b.length; i += 16) {
					if (b[i] == 110 && b[i+1] == 97 && b[i+2] == 109 && b[i+3] == 101) {
						// name table found
						for (int j = 0; j < 4; j++) {
							long tmp = ((long) b[i + 8 + j] & 0xFF) & 0xffffffff;
							tmp = tmp << ((3 - j) * 8);
							offset += tmp;
							
							tmp = 0;
							tmp = ((long) b[i + 12 + j] & 0xFF) & 0xffffffff;
							tmp = tmp << ((3 - j) * 8);
							length += tmp;
						}
					}
				}
				
				// reload from offset
				rf2.seek(0);
				rf2.seek(offset);
				b = null;
				b = new byte[(int) length];
				rf2.readFully(b);
				
				// read name table's count
				for (int j = 0; j < 2; j++) {
					long tmp = ((long)( b[j + 2] & 0xFF)) & 0xffffffff;
					tmp = tmp << ((1 - j) * 8);
					count += tmp;
				}
				
				// get platform id and language id from each name record
				for (int i = 0; i < count; i ++) {
					platformID = 0;
					languageID = 0;
					for (int j = 0; j < 2; j++) {
						long tmp = ((long) b[(int) (6 + (i*12) + 0 + j)] & 0xFF) & 0xffffffff;
						tmp = tmp << ((1 - j) * 8);
						platformID += tmp;
					
						
						tmp = ((long) b[(int) (6 + (i*12)  + 4 + j)] & 0xFF) & 0xffffffff;
						tmp = tmp << ((1 - j) * 8);
						languageID += tmp;
					}
					
					if (platformID == 3 && languageID == 1042) {
						// windows && Korean
						return true;
					} else if (platformID == 1 && languageID == 23) {
						// Macintosh && Korean
						return true;
					} else continue;
				}
			} finally {
				try {
					rf2.close();
				} catch (Exception e) {
					// empty on purpose
					throw e;
				}
			}
		}// end of synch
		
		// not Korean
		return false;
	}// end of isKorean
	
	
	/**
	 * get PANOSE value from fontfile's OS/2 table
	 * 
	 * @param fontFileName
	 * 			source font file's name
	 * @return
	 * 			panose value as string
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static String getPANOSE(String fontFileName) throws DocumentException, IOException {

		TrueTypeFontUnicode font = (TrueTypeFontUnicode) BaseFont.createFont(fontFileName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		String ret = "";
		synchronized (font.rf) {
			@SuppressWarnings("deprecation")
			RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(font.rf);
			long len = rf2.length();
			long offset = 0;
			long length = 0;
			
			
			byte[] b = new byte[(int)len];
			try {
				rf2.seek(0);
				rf2.readFully(b);
				
				// read offset and length of OS/2 table
				for (int i = 12; i < b.length; i += 16) {
					if (b[i] == 79 && b[i+1] == 83 && b[i+2] == 47 && b[i+3] == 50) {
						// name table found
						for (int j = 0; j < 4; j++) {
							long tmp = ((long) b[i + 8 + j] & 0xFF) & 0xffffffff;
							tmp = tmp << ((3 - j) * 8);
							offset += tmp;
							
							tmp = 0;
							tmp = ((long) b[i + 12 + j] & 0xFF) & 0xffffffff;
							tmp = tmp << ((3 - j) * 8);
							length += tmp;
						}
					}
				}
				
				// reload from offset
				rf2.seek(0);
				rf2.seek(offset + 32);
				b = null;
				b = new byte[(int) length];
				rf2.readFully(b);
				
				
				// get platform id and language id from each name record
				for (int i = 0; i < 10; i ++) {
					ret += (b[i] + "");
				}
			} finally {
				try {
					rf2.close();
				} catch (Exception e) {
					// empty on purpose
					throw e;
				}
			}
		}// end of synch
		
		// not Korean
		return ret;
	}// end of getPANOSE



	public SubsetFont(String fontFileName, String text) throws DocumentException, IOException  {
		super();
		this.fontFileName = fontFileName;
		this.text = text;
		this.bf = BaseFont.createFont(fontFileName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		
		font = (TrueTypeFontUnicode) bf;
		longTag = new HashMap<Integer, int[]>();
		
		parseSourceFontPath();
		extractUsedGlyphs();
	}



	/**
	 * method to bring actulfilename which includes only filename and excludes path delimiter and path
	 * also get extension of file and path of directory
	 * @param fontFileName
	 */
	private void parseSourceFontPath() {

		// get file actual filename
		if (fontFileName.contains("\\")) {
			// "\\\\" 인 이유는 정규표현식을 위해서이다. 뒤에서 "\\"로 다시 바꿔준다.
			pathDelimiter = "\\\\";
		} else if (fontFileName.contains("/")) {
			pathDelimiter = "/";
		} else {
			pathDelimiter = "";
		}
		
		String[] chunks = fontFileName.split(pathDelimiter);
		actualFileName = chunks[chunks.length - 1];
		
		if (pathDelimiter.length() > 0) {
			directory = fontFileName.substring(0, fontFileName.length() - actualFileName.length() - 1);	
		} else {
			directory = "";	
		}
		
		chunks = actualFileName.split("\\.");
		extension = chunks[chunks.length -1];
		
		if (pathDelimiter.equals("")) {
			// path 구분자가 없을 경우 임의로 "/" 를 추가해준다.
			pathDelimiter = "/";
		} else if (pathDelimiter.equals("\\\\")) {
			pathDelimiter = "\\";
		}
		
	}
	
	/**
	 * save to longTag with cmap data after extract glyphs which used in text
	 * @param text
	 * @param longTag
	 * @return
	 */
	private void extractUsedGlyphs() {
		
		int len = text.length();
		int metrics[] = null;
		
		for (int k = 0; k < len; ++k) {
			int val;
			if (Utilities.isSurrogatePair(text, k)) {
				// never saw this case yet
				val = Utilities.convertToUtf32(text, k);
				k++;
			} else {
				val = text.charAt(k);
			}

			// Gets the glyph index and metrics for a character.
			metrics = font.getMetricsTT(val);
			if (metrics == null)
				continue;
			int m0 = metrics[0];
			Integer gl = Integer.valueOf(m0);

			// if longTage has overlapping data then doen not put it into longTag 
			// longTag is the set of glyphs used in given text
			if (!longTag.containsKey(gl))
				longTag.put(gl, new int[] { m0, metrics[1], val });

		}
		return ;
	}



	public TrueTypeFontUnicode getFont() {
		return font;
	}



	public void setFont(TrueTypeFontUnicode font) {
		this.font = font;
	}



	public HashMap<Integer, int[]> getLongTag() {
		return longTag;
	}



	public void setLongTag(HashMap<Integer, int[]> longTag) {
		this.longTag = longTag;
	}



	public String getActualFileName() {
		return actualFileName;
	}



	public void setActualFileName(String actualFileName) {
		this.actualFileName = actualFileName;
	}



	public String getPathDelimiter() {
		return pathDelimiter;
	}



	public void setPathDelimiter(String pathDelimiter) {
		this.pathDelimiter = pathDelimiter;
	}



	public String getExtension() {
		return extension;
	}



	public String getDirectory() {
		return directory;
	}


	
	
	
	
}
